/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of Penumbra Overture.
 *
 * Penumbra Overture is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Penumbra Overture is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Penumbra Overture.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "HudModel_Weapon.h"

#include "Init.h"
#include "Player.h"
#include "PlayerHelper.h"
#include "AttackHandler.h"
#include "GameEntity.h"
#include "GameEnemy.h"
#include "MapHandler.h"
#include "EffectHandler.h"

#include "GlobalInit.h"


//////////////////////////////////////////////////////////////////////////
// MELEE RAY CALLBACK
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

void cMeleeRayCallback::Reset()
{
	mpClosestBody = NULL;
}

//-----------------------------------------------------------------------

bool cMeleeRayCallback::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams)
{
	if(pBody->GetCollide()==false) return true;
	if(pBody->IsCharacter()) return true;
	
	if(apParams->mfDist < mfShortestDist || mpClosestBody == NULL)
	{
		mpClosestBody = pBody;
		mfShortestDist = apParams->mfDist;
		mvPosition = apParams->mvPoint;
		mvNormal = apParams->mvNormal;
	}

	return true;
}

//-----------------------------------------------------------------------


//////////////////////////////////////////////////////////////////////////
// HUD MODEL MELEE WEAPON
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cHudModel_WeaponMelee::cHudModel_WeaponMelee() : iHudModel(ePlayerHandType_WeaponMelee)
{
	ResetExtraData();

	if(gpInit->mbHasHaptics)
	{
		mpLowLevelHaptic = gpInit->mpGame->GetHaptic()->GetLowLevel();
		mpHHitForce = mpLowLevelHaptic->CreateSinusWaveForce(cVector3f(0,1,0),0.63f,5);
		mpHHitForce->SetActive(false);
	}
	else
	{
		mpLowLevelHaptic = NULL;
	}
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::LoadData(TiXmlElement *apRootElem)
{
	////////////////////////////////////////////////
	//Load the MAIN element.
	TiXmlElement *pMeleeElem = apRootElem->FirstChildElement("MELEE");
	if(pMeleeElem==NULL){
		Error("Couldn't load MELEE element from XML document\n");
		return;
	}

	mvHapticSize = cString::ToVector3f(pMeleeElem->Attribute("HapticSize"),0);
	mvHapticRot = cString::ToVector3f(pMeleeElem->Attribute("HapticRotate"),0);
	mfHapticScale = cString::ToFloat(pMeleeElem->Attribute("HapticScale"),2);

	mvHapticRot.x = cMath::ToRad(mvHapticRot.x);
	mvHapticRot.y = cMath::ToRad(mvHapticRot.y);
	mvHapticRot.z = cMath::ToRad(mvHapticRot.z);

	
	mbDrawDebug = cString::ToBool(pMeleeElem->Attribute("DrawDebug"),false);

	////////////////////////////////////////////////
	//Go through the ATTACK elements.
	TiXmlElement *pAttackElem = apRootElem->FirstChildElement("ATTACK");
	for(; pAttackElem != NULL; pAttackElem = pAttackElem->NextSiblingElement("ATTACK"))
	{
		cMeleeWeaponAttack meleeAttack;

        meleeAttack.mStart = GetPoseFromElem("StartPose",pAttackElem);
		meleeAttack.mEnd = GetPoseFromElem("EndPose",pAttackElem);
		meleeAttack.mfAttackLength = cString::ToFloat(pAttackElem->Attribute("AttackLength"),0);
		meleeAttack.mfChargeLength = cString::ToFloat(pAttackElem->Attribute("ChargeLength"),0);
		meleeAttack.mfTimeOfAttack = cString::ToFloat(pAttackElem->Attribute("TimeOfAttack"),0);

		meleeAttack.mfMaxImpulse = cString::ToFloat(pAttackElem->Attribute("MaxImpulse"),0);
		meleeAttack.mfMinImpulse = cString::ToFloat(pAttackElem->Attribute("MinImpulse"),0);
		meleeAttack.mfMinMass = cString::ToFloat(pAttackElem->Attribute("MinMass"),0);
		meleeAttack.mfMaxMass = cString::ToFloat(pAttackElem->Attribute("MaxMass"),0);
		meleeAttack.mfMinDamage = cString::ToFloat(pAttackElem->Attribute("MinDamage"),0);
		meleeAttack.mfMaxDamage = cString::ToFloat(pAttackElem->Attribute("MaxDamage"),0);

		meleeAttack.msSwingSound = cString::ToString(pAttackElem->Attribute("SwingSound"),"");
		meleeAttack.msChargeSound = cString::ToString(pAttackElem->Attribute("ChargeSound"),"");
		meleeAttack.msHitSound = cString::ToString(pAttackElem->Attribute("HitSound"),"");
		
		meleeAttack.mvSpinMul = cString::ToVector3f(pAttackElem->Attribute("SpinMul"),0);

		meleeAttack.mfDamageRange = cString::ToFloat(pAttackElem->Attribute("DamageRange"),0);
		meleeAttack.mvDamageSize = cString::ToVector3f(pAttackElem->Attribute("DamageSize"),0);

		meleeAttack.mfAttackRange = cString::ToFloat(pAttackElem->Attribute("AttackRange"),0);

		meleeAttack.mfAttackSpeed = cString::ToFloat(pAttackElem->Attribute("AttackSpeed"),0);
		meleeAttack.mlAttackStrength = cString::ToInt(pAttackElem->Attribute("AttackStrength"),0);

		meleeAttack.msHitPS = cString::ToString(pAttackElem->Attribute("HitPS"),"");
		meleeAttack.mlHitPSPrio = cString::ToInt(pAttackElem->Attribute("HitPSPrio"),0);
	
		//Get largest side and use that to make bounding box.
		float fMax = meleeAttack.mvDamageSize.x;
		if(fMax < meleeAttack.mvDamageSize.y) fMax = meleeAttack.mvDamageSize.y;
		if(fMax < meleeAttack.mvDamageSize.z) fMax = meleeAttack.mvDamageSize.z;

		meleeAttack.mBV.SetSize(fMax * kSqrt2f);

		mvAttacks.push_back(meleeAttack);
	}
}

//-----------------------------------------------------------------------

bool cHudModel_WeaponMelee::UpdatePoseMatrix(cMatrixf& aPoseMtx, float afTimeStep)
{
	////////////////////////
	//Idle and waiting for movement
	if(mlAttackState<=1)
	{
		return false;
	}
	////////////////////
	//Movement
	else
	{
		aPoseMtx = cMath::MatrixSlerp(mfTime,m_mtxPrevPose,m_mtxNextPose,true);
		
		float fMul = 1.0f;
		//if(mlAttackState == 2 && mpInit->mDifficulty== eGameDifficulty_Easy) fMul = 1.6f;

		mfTime += mfMoveSpeed * afTimeStep * fMul;
		
		//Attack
		if(mlAttackState == 4 &&mfTime >= mvAttacks[mlCurrentAttack].mfTimeOfAttack && mbAttacked==false)
		{
			Attack();
			mbAttacked = true;
		}

		//Time is up
		if(mfTime >= 1.0f)
		{
			mfTime =1.0f;
			

			switch(mlAttackState)
			{
			case 2:
				mlAttackState = 3;
				break;
			case 4:
				mlAttackState = 5;
				mbAttacked = false;

				m_mtxPrevPose = m_mtxNextPose;
				m_mtxNextPose = mEquipPose.ToMatrix();

				mfMoveSpeed = 2;
				mfTime =0;

				break;
			case 5:
				if(mbButtonDown)	mlAttackState = 1;
				else				mlAttackState = 0;
								
				break;
			}

		}

		return true;
	}

	return false;
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::OnAttackDown()
{
	if(mState == eHudModelState_Idle && mlAttackState ==0)
	{
		mlAttackState = 1;
		mfTime =0;

		mbButtonDown = true;
	}
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::OnAttackUp()
{
	if(mpInit->mbSimpleWeaponSwing)
	{

	}
	else
	{
		if(mlAttackState != 0 && mlAttackState != 4 && mlAttackState != 5)
		{
			mlAttackState = 5;

			mfMoveSpeed = 2;
			mfTime =0;

			m_mtxPrevPose = m_mtxNextPose;
			m_mtxNextPose = mEquipPose.ToMatrix();
		}
	}

	mbButtonDown = false;
}

//-----------------------------------------------------------------------

bool cHudModel_WeaponMelee::OnMouseMove(const cVector2f &avMovement)
{
	float fMinMovement = 0.015f;

	if(mlAttackState ==0 || (mbButtonDown==false && mpInit->mbSimpleWeaponSwing==false))
	{
		return true;
	}
	else
	{
		/////////////////////////////
		//Check for charge
		if(mlAttackState == 1)
		{
			if(mpInit->mbSimpleWeaponSwing)
			{
				//if(avMovement.y < -0.03f)
				//	mlCurrentAttack = 2;
				//else
					mlCurrentAttack = 0;//cMath::RandRectl(0,1);
				
				mlAttackState =2;
			}
			//Right charge
			else if(avMovement.x > fMinMovement)
			{
				mlCurrentAttack = 0;
				mlAttackState = 2;
			}
			//Left charge
			else if(avMovement.x < -fMinMovement)
			{
				mlCurrentAttack = 1;
				mlAttackState = 2;
			}
			//Down charge
			else if(avMovement.y > fMinMovement)
			{
				mlCurrentAttack = 2;
				mlAttackState = 2;
			}
			
			//Go to charge
			if(mlAttackState==2)
			{
				mfTime = 0.0f;
				mfMoveSpeed = 1/mvAttacks[mlCurrentAttack].mfChargeLength;

				//if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
				//	mfMoveSpeed *= 0.8f;

				PlaySound(mvAttacks[mlCurrentAttack].msChargeSound);
				
				m_mtxPrevPose = mEquipPose.ToMatrix();
				m_mtxNextPose = mvAttacks[mlCurrentAttack].mStart.ToMatrix();
			}
			
		}
		else if(mlAttackState == 3)
		{
			//If right key is down enable looking.
			cInput *pInput = mpInit->mpGame->GetInput();
			if(pInput->IsTriggerd("Examine")) return true;
			
			if(mpInit->mbSimpleWeaponSwing)
			{
				if(mlCurrentAttack != 2 && pInput->IsTriggerd("Interact")==false)
				{
					mfTime = 0.0f;
					mfMoveSpeed = 1/mvAttacks[mlCurrentAttack].mfChargeLength;
					//if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch) 	mfMoveSpeed *= 0.8f;
					
					m_mtxPrevPose = mvAttacks[mlCurrentAttack].mStart.ToMatrix();
					m_mtxNextPose = mvAttacks[2].mStart.ToMatrix();

					mlCurrentAttack = 2;
					mlAttackState = 2;
				}
				else
				{
					mlAttackState = 4;
				}
			}
			else if(mlCurrentAttack==0)
			{
				if(avMovement.x < -fMinMovement)
				{
					mlAttackState = 4;
				}
			}
			else if(mlCurrentAttack==1)
			{
				if(avMovement.x > fMinMovement)
				{
					mlAttackState = 4;
				}
			}
			else if(mlCurrentAttack==2)
			{
				if(avMovement.y < -fMinMovement)
				{
					mlAttackState = 4;
				}
			}

			if(mlAttackState == 4)
			{
				mfTime = 0.0f;
				mfMoveSpeed = 1.0f/mvAttacks[mlCurrentAttack].mfAttackLength;

				//if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
				//	mfMoveSpeed *= 0.55f;
				
				PlaySound(mvAttacks[mlCurrentAttack].msSwingSound);

				mpInit->mpPlayer->GetHidden()->UnHide();

				m_mtxPrevPose = m_mtxNextPose;
				m_mtxNextPose = mvAttacks[mlCurrentAttack].mEnd.ToMatrix();
			}
		}
		
		return mpInit->mbSimpleWeaponSwing;
	}
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::PlaySound(const tString &asSound)
{
	cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

	pSoundHandler->PlayGui(asSound,false,1.0f);
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::LoadExtraEntites()
{
	iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
	
	for(size_t i=0; i< mvAttacks.size(); ++i)
	{
		//Attack shapes
		mvAttacks[i].mpCollider = pWorld->CreateBoxShape(mvAttacks[i].mvDamageSize,NULL);

		//Preload particle system
		mpInit->PreloadParticleSystem(mvAttacks[i].msHitPS);
		
		//Preload sounds
		mpInit->PreloadSoundEntityData(mvAttacks[i].msHitSound);
		mpInit->PreloadSoundEntityData(mvAttacks[i].msSwingSound);
		mpInit->PreloadSoundEntityData(mvAttacks[i].msChargeSound);
	}

	
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::DestroyExtraEntities()
{
	iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();

	for(size_t i=0; i< mvAttacks.size(); ++i)
	{
		if(mvAttacks[i].mpCollider)
			pWorld->DestroyShape(mvAttacks[i].mpCollider);
	}
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::PostSceneDraw()
{
	if(mbDrawDebug==false) return;

	cCamera3D *pCamera = static_cast<cCamera3D*>(mpInit->mpGame->GetScene()->GetCamera());
	float fAttackRange = mvAttacks[mlCurrentAttack].mfAttackRange;	

	cVector3f vPos = pCamera->GetPosition() + pCamera->GetForward()*fAttackRange;
	mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(vPos,0.1f,cColor(1,0,1,1));
		
	//return;
	
	float fDamageRange = mvAttacks[mlCurrentAttack].mfDamageRange;	
	cVector3f vCenter = pCamera->GetPosition() + pCamera->GetForward()*fDamageRange;

	cMatrixf mtxDamage = cMath::MatrixRotate(
								cVector3f(pCamera->GetPitch(),pCamera->GetYaw(),pCamera->GetRoll()),
								eEulerRotationOrder_XYZ);
	mtxDamage.SetTranslation(vCenter);

	bool bCollide=false;
	/*{
		cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
		iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();
		
		bCollide = pPhysicsWorld->CheckShapeWorldCollision(NULL,mvAttacks[mlCurrentAttack].mpCollider,
															mtxDamage,NULL,false,false,NULL,false);
	}*/


	
	cMatrixf mtxCollider = cMath::MatrixMul(pCamera->GetViewMatrix(),mtxDamage);
		
	mpInit->mpGame->GetGraphics()->GetLowLevel()->SetMatrix(eMatrix_ModelView,mtxCollider);

	cVector3f vSize = mvAttacks[mlCurrentAttack].mvDamageSize;
	
	if(bCollide)
		mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize*0.5f,vSize*-0.5f,
																cColor(0,1,0,1));
	else
		mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize*0.5f,vSize*-0.5f,
																cColor(1,0,1,1));
}

//-----------------------------------------------------------------------

bool cHudModel_WeaponMelee::IsAttacking()
{
	if(mlAttackState >1) return true;

	return false;
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::ResetExtraData()
{
	mlAttackState = 0;
	mfTime =0;

	mlCurrentAttack =0;

	mbButtonDown = false;
	mbAttacked = false;

	m_mtxPrevPose = cMatrixf::Identity;
	m_mtxNextPose = cMatrixf::Identity;

	mfMoveSpeed = 1.0f;
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::Attack()
{
	mpInit->mbWeaponAttacking = true;
	//Log("----------------- BEGIN ATTACK WITH WEAPON ------------ \n");

	////////////////////////////////
	//Set up
	float fDamageRange = mvAttacks[mlCurrentAttack].mfDamageRange;	

	float fMaxImpulse = mvAttacks[mlCurrentAttack].mfMaxImpulse;
	float fMinImpulse = mvAttacks[mlCurrentAttack].mfMinImpulse;

	float fMaxMass = mvAttacks[mlCurrentAttack].mfMaxMass;
	float fMinMass = mvAttacks[mlCurrentAttack].mfMinMass;

	
	cCamera3D *pCamera =mpInit->mpPlayer->GetCamera(); 
	cVector3f vCenter = pCamera->GetPosition() + pCamera->GetForward()*fDamageRange;

	cBoundingVolume tempBV = mvAttacks[mlCurrentAttack].mBV;
	tempBV.SetPosition(vCenter);

	cVector3f vSpinMul = cVector3f(0, 1.0f, 0.0f);
	vSpinMul =	pCamera->GetRight() * vSpinMul.x +
				pCamera->GetUp() * vSpinMul.y +
				pCamera->GetForward() * vSpinMul.z;

	cMatrixf mtxDamage = cMath::MatrixRotate(
		cVector3f(pCamera->GetPitch(),pCamera->GetYaw(),pCamera->GetRoll()),
		eEulerRotationOrder_XYZ);
	mtxDamage.SetTranslation(vCenter);

	cCollideData collideData;
	collideData.SetMaxSize(1);
	    
	bool bHit = false;

	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();

	tVector3fList lstPostions;

	////////////////////////////////
	//Iterate Enemies
	tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator();
	while(enemyIt.HasNext())
	{
		iGameEnemy *pEnemy = enemyIt.Next();
		iPhysicsBody *pBody = pEnemy->GetMover()->GetCharBody()->GetBody();
		float fMass = pBody->GetMass();

		if(pEnemy->GetMover()->GetCharBody()->IsActive()==false) continue;

		if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV()))
		{
			/*if(pPhysicsWorld->CheckShapeCollision(pBody->GetShape(),pBody->GetLocalMatrix(),
				mvAttacks[mlCurrentAttack].mpCollider,
				mtxDamage,collideData,1)==false)
			{
				continue;
			}*/
			if(pEnemy->GetMeshEntity()->CheckColliderShapeCollision(pPhysicsWorld,
																	mvAttacks[mlCurrentAttack].mpCollider,
																	mtxDamage,&lstPostions,NULL)==false)
			{
				continue;
			}

			//Calculate force
			float fForceSize =0;
			if(fMass > fMaxMass) fForceSize =0;
			else if(fMass <= fMinMass) fForceSize = fMaxImpulse;
			else{
				float fT = (fMass - fMinMass) / (fMaxMass - fMinMass);
				fForceSize = fMinImpulse * fT + fMaxImpulse * (1-fT);
			}

			cVector3f vForceDir = pCamera->GetForward();
			vForceDir.Normalise();
			
			//Add force to bodies
			for(int i=0; i < pEnemy->GetBodyNum(); ++i)
			{
				iPhysicsBody* pBody = pEnemy->GetBody(i);
				
				pBody->AddImpulse(vForceDir *fForceSize*0.5f);

				cVector3f vTorque = vSpinMul * fMass * fForceSize *0.5f;
				pBody->AddTorque(vTorque);
			}

			//Calculate damage
			float fDamage = cMath::RandRectf(	mvAttacks[mlCurrentAttack].mfMinDamage,
												mvAttacks[mlCurrentAttack].mfMaxDamage);

			pEnemy->Damage(fDamage,mvAttacks[mlCurrentAttack].mlAttackStrength);
			
			//Get closest position
			float fClosestDist = 9999.0f;
			cVector3f vClosestPostion = vCenter;
			for(tVector3fListIt it = lstPostions.begin(); it != lstPostions.end(); ++it)
			{
				cVector3f &vPos = *it;
				
				float fDist = cMath::Vector3DistSqr(pCamera->GetPosition(),vPos);
				if(fDist < fClosestDist)
				{
					fClosestDist = fDist;
					vClosestPostion = vPos;
				}
			}
			
			//Particle system
			if(pEnemy->GetHitPS()!="")
			{
				pWorld->CreateParticleSystem("Hit",pEnemy->GetHitPS(),1,
											cMath::MatrixTranslate(vClosestPostion));
			}

			lstPostions.clear();
		
			bHit = true;
		}
	}
	
	std::set<iPhysicsBody*> m_setHitBodies;

	////////////////////////////////
	//Iterate bodies
	float fClosestHitDist = 9999.0f;
	cVector3f vClosestHitPos;
	iPhysicsMaterial* pClosestHitMat = NULL;
	cPhysicsBodyIterator it = pPhysicsWorld->GetBodyIterator();
	while(it.HasNext())
	{
		iPhysicsBody *pBody = it.Next();

		float fMass = pBody->GetMass();

		if(pBody->IsActive()==false) continue;
		if(pBody->GetCollide()==false) continue;
		if(pBody->IsCharacter()) continue;

		
		if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV()))
		{
			if(pPhysicsWorld->CheckShapeCollision(pBody->GetShape(),pBody->GetLocalMatrix(),
											mvAttacks[mlCurrentAttack].mpCollider,
											mtxDamage,collideData,1)==false)
			{
				continue;
			}

			cVector3f vHitPos = collideData.mvContactPoints[0].mvPoint;
			
			//Check if collision is in line of sight
			{
				mRayCallback.Reset();
				cVector3f vRayStart = pCamera->GetPosition();
				cVector3f vRayEnd = vHitPos;

				pPhysicsWorld->CastRay(&mRayCallback,vRayStart,vRayEnd,true,true,true,false);

				if(mRayCallback.mpClosestBody &&
					mRayCallback.mpClosestBody != pBody)
				{
					continue;
				}
			}

			m_setHitBodies.insert(pBody);
			
			//Deal damage and force
			HitBody(pBody);
			
			//Check if this is the closest hit body
			float fDist = cMath::Vector3DistSqr(vHitPos, pCamera->GetPosition());
			if(fDist < fClosestHitDist)
			{
				fClosestHitDist = fDist;
				vClosestHitPos = collideData.mvContactPoints[0].mvPoint;

				pClosestHitMat = pBody->GetMaterial();
			}
			
			bHit = true;
		}
	}

	////////////////////////////////////////////
	//Check with ray and see a closer material can be found.
	{
		float fAttackRange = mvAttacks[mlCurrentAttack].mfAttackRange;

		mRayCallback.Reset();
		cVector3f vRayStart = pCamera->GetPosition();
		cVector3f vRayEnd = pCamera->GetPosition() + pCamera->GetForward()*fAttackRange;

		pPhysicsWorld->CastRay(&mRayCallback,vRayStart,vRayEnd,true,true,true,false);

		if(mRayCallback.mpClosestBody)
		{
			//Use ray cast to check hit as well
			//Check first if body has not allready been hit.
			if(m_setHitBodies.find(mRayCallback.mpClosestBody)==m_setHitBodies.end())
			{
				HitBody(mRayCallback.mpClosestBody);
			}

			float fDist = cMath::Vector3DistSqr(mRayCallback.mvPosition, pCamera->GetPosition());
			if(fDist < fClosestHitDist)
			{
				fClosestHitDist = fDist;
				vClosestHitPos = mRayCallback.mvPosition;
				pClosestHitMat = mRayCallback.mpClosestBody->GetMaterial();
			}
		}
	}
		
	////////////////////////////////////////////
	//Check the closest material and play sounds and effects depending on it.
	if(pClosestHitMat)
	{
		bHit = true;

		cMatrixf mtxPosition = cMath::MatrixTranslate(vClosestHitPos);

		cSurfaceData *pData = pClosestHitMat->GetSurfaceData();
		cSurfaceImpactData *pImpact = pData->GetHitDataFromSpeed(mvAttacks[mlCurrentAttack].mfAttackSpeed);
		if(pImpact)
		{
			cSoundEntity *pSound = pWorld->CreateSoundEntity("Hit",pImpact->GetSoundName(),true);
			if(pSound) pSound->SetPosition(vClosestHitPos);

			if(mvAttacks[mlCurrentAttack].mlHitPSPrio <= pImpact->GetPSPrio())
			{
				if(pImpact->GetPSName()!="")
					pWorld->CreateParticleSystem("Hit",pImpact->GetPSName(),1,mtxPosition);
			}
			else
			{
				if(mvAttacks[mlCurrentAttack].msHitPS!="")
					pWorld->CreateParticleSystem("Hit",mvAttacks[mlCurrentAttack].msHitPS,1,mtxPosition);
			}
		}
	}
	//Log("----------------- END ATTACK WITH WEAPON ------------ \n");
	
	/////////////////////////
	//Play hit sound
	if(bHit)
	{
		PlaySound(mvAttacks[mlCurrentAttack].msHitSound);

		if(mpInit->mbHasHaptics)
		{
			if(mpHHitForce->IsActive())	mpHHitForce->SetActive(false);
			
			mpHHitForce->SetActive(true);
			mpHHitForce->SetTimeControl(false,0.3f, 0.2f, 0,0.1f);
		}
	}

	mpInit->mbWeaponAttacking = false;
}

//-----------------------------------------------------------------------

void cHudModel_WeaponMelee::HitBody(iPhysicsBody *apBody)
{
	iGameEntity *pEntity = (iGameEntity*)apBody->GetUserData();

	if(pEntity && pEntity->GetType() == eGameEntityType_Enemy) return;
	
	cCamera3D *pCamera =mpInit->mpPlayer->GetCamera(); 

	cVector3f vSpinMul = mvAttacks[mlCurrentAttack].mvSpinMul;
	vSpinMul =	pCamera->GetRight() * vSpinMul.x +
				pCamera->GetUp() * vSpinMul.y +
				pCamera->GetForward() * vSpinMul.z;
	
	float fMass = apBody->GetMass();

	float fMaxImpulse = mvAttacks[mlCurrentAttack].mfMaxImpulse;
	float fMinImpulse = mvAttacks[mlCurrentAttack].mfMinImpulse;

	float fMaxMass = mvAttacks[mlCurrentAttack].mfMaxMass;
	float fMinMass = mvAttacks[mlCurrentAttack].mfMinMass;

	//Calculate force
	float fForceSize =0;
	if(fMass > fMaxMass) fForceSize =0;
	else if(fMass <= fMinMass) fForceSize = fMaxImpulse;
	else{
		float fT = (fMass - fMinMass) / (fMaxMass - fMinMass);
		fForceSize = fMinImpulse * fT + fMaxImpulse * (1-fT);
	}

	//Calculate damage
	float fDamage = cMath::RandRectf(	mvAttacks[mlCurrentAttack].mfMinDamage,
		mvAttacks[mlCurrentAttack].mfMaxDamage);

	cVector3f vForceDir = pCamera->GetForward();

	if(fMass>0 && fForceSize >0)
	{
		vForceDir.Normalise();

		//pBody->AddForce(vForceDir * fForceSize);
		apBody->AddImpulse(vForceDir *fForceSize);

		cVector3f vTorque = vSpinMul * fMass * fForceSize;

		//vTorque = cMath::MatrixMul(pBody->GetInertiaMatrix(),vTorque);

		apBody->AddTorque(vTorque);
	}

	if(pEntity)
	{
		pEntity->SetLastImpulse(vForceDir *fForceSize);
		pEntity->Damage(fDamage,mvAttacks[mlCurrentAttack].mlAttackStrength);
	}
}

//-----------------------------------------------------------------------
